package com.spring.framework.aop.support;

import com.spring.framework.aop.aspect.MyAdvice;
import com.spring.framework.aop.config.MyAopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ππ
 * @date 2020-11-18 11:21
 * @desc 声明支持类
 */

public class MyAdviceSupport {
    /**保存配置类*/
    private MyAopConfig aopConfig;
    /**保存目标对象*/
    private Object targetInstance;
    /**保存目标类*/
    private Class<?> targetClass;
    /**存储Method与Advice映射*/
    private Map<Method,Map<String, MyAdvice>> methodCache = new HashMap<>();
    /**类匹配的正则表达式*/
    private Pattern pointCutClassPattern;
    public MyAdviceSupport(MyAopConfig aopConfig) {
        this.aopConfig = aopConfig;
    }

    public Object getTargetInstance() {
        return targetInstance;
    }

    public void setTargetInstance(Object targetInstance) {
        this.targetInstance = targetInstance;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        // 存在性能问题
        parse();
    }

    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

    public Map<String, MyAdvice> getAdvices(Method method) throws NoSuchMethodException {
        Map<String,MyAdvice> cache = methodCache.get(method);
        if(cache == null) {
            Method m = targetClass.getMethod(method.getName(),method.getParameterTypes());
            cache = methodCache.get(m);
            this.methodCache.put(m,cache);
        }
        return cache;
    }
    private void parse() {
        // 把Spring的Expression表达式转化为Java能识别的正则表达式
        String pointCut = aopConfig.getPointCut().replaceAll("\\.","\\\\.")
                .replaceAll("\\\\.\\*",".*")
                .replaceAll("\\(","\\\\(")
                .replaceAll("\\)","\\\\)");
        String pointCutForClassRegex = pointCut.substring(0,pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        methodCache = new HashMap<>();
        //方法匹配表达式
        Pattern pointCutPattern = Pattern.compile(pointCut);
        try {
            //缓存AspectClass中的方法，简单粗暴的方法，可以进行缓存
            Class<?> aspectClass = Class.forName(aopConfig.getAspectClass());
            Map<String,Method> aspectMethod = new HashMap<>();
            for(Method method : aspectClass.getMethods()) {
                aspectMethod.put(method.getName(),method);
            }
            for(Method method : this.targetClass.getMethods()) {
                String methodString  = method.toString();
                if(methodString.indexOf("throws") >-1){
                    methodString = methodString.substring(0,methodString.indexOf("throws")).trim();
                }
                // 先进行硬编码
                Matcher matcher = pointCutPattern.matcher(methodString);
                if(matcher.matches()) {
                    Map<String,MyAdvice> advices = new HashMap<>();
                    if(!(aopConfig.getAspectBefore() == null || "".equals(aopConfig.getAspectBefore()))) {
                        advices.put("before",new MyAdvice(aspectClass.newInstance(),aspectMethod.get(aopConfig.getAspectBefore())));
                    }
                    if(!(aopConfig.getAspectAfter() == null || "".equals(aopConfig.getAspectAfter()))) {
                        advices.put("after",new MyAdvice(aspectClass.newInstance(),aspectMethod.get(aopConfig.getAspectAfter())));
                    }
                    if(!(aopConfig.getAspectAfterThrow() == null || "".equals(aopConfig.getAspectAfterThrow()))) {
                        MyAdvice advice = new MyAdvice(aspectClass.newInstance(),aspectMethod.get(aopConfig.getAspectAfterThrow()));
                        advice.setThrowName(aopConfig.getAspectAfterThrowName());
                        advices.put("afterThrowing",advice);
                    }
                    methodCache.put(method,advices);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
